﻿using CrazyMVSamLibrary.Function;
using CrazyMVSamLibrary.Model;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Microsoft.Win32;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace CrazyMVSample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        SamFunction sam = new SamFunction();
        string encode_model_path = System.IO.Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + @"\OnnxModel\encoder-quant.onnx";
        string decode_model_path = System.IO.Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + @"\OnnxModel\decoder-quant.onnx";
        Mat mat = null;

        Path box = new Path { Stroke = System.Windows.Media.Brushes.Red, StrokeThickness = 2, Fill = System.Windows.Media.Brushes.AliceBlue, Opacity = 0.5 };
        bool isDown = false;
        DrawType drawType = DrawType.None;
        System.Windows.Point downPoint;
        List<Path> addPoint = new List<Path>();
        List<Path> removePoint = new List<Path>();

        private BitmapImage BitmapToBitmapImage(Bitmap bitmap)
        {
            BitmapImage bitmapImage = new BitmapImage();
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            bitmap.Save(ms, ImageFormat.Bmp);
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = ms;
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.EndInit();
            bitmapImage.Freeze();
            ms.Dispose();
            return bitmapImage;
        }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.sam.LoadModel(this.encode_model_path, this.decode_model_path);
        }

        private void Window_Closing(object sender, CancelEventArgs e)
        {
            this.sam.Clear();
        }

        private void Image_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "All files (*.*)|*.*";
            if (openFileDialog.ShowDialog() == true)
            {
                foreach (Path item in this.addPoint)
                {
                    this.imageGrid.Children.Remove(item);
                }
                foreach (Path item in this.removePoint)
                {
                    this.imageGrid.Children.Remove(item);
                }
                this.addPoint.Clear();
                this.removePoint.Clear();
                this.imageGrid.Children.Remove(this.box);
                Task.Run(() =>
                {
                    this.border.Dispatcher.Invoke(() =>
                    {
                        this.bar.Visibility = Visibility.Visible;
                        this.border.Visibility = Visibility.Visible;
                    });
                    string filePath = openFileDialog.FileName;
                    if (this.mat != null)
                    {
                        this.mat.Dispose();
                        this.sam.Clear();
                        this.sam.LoadModel(this.encode_model_path, this.decode_model_path);
                    }
                    this.mat = CvInvoke.Imread(filePath, ImreadModes.Unchanged);
                    if (mat.NumberOfChannels == 3)
                    {
                        Image<Bgr, byte> image = mat.ToImage<Bgr, byte>();
                        this.sam.EncodeImage(image.Data, image.Width, image.Height);
                        image.Dispose();
                    }
                    else
                    {
                        Image<Gray, byte> image = mat.ToImage<Gray, byte>();
                        this.sam.EncodeImage(image.Data, image.Width, image.Height);
                        image.Dispose();
                        Mat result = mat.ToImage<Bgr, byte>().Mat;
                        mat.Dispose();
                        mat = result;
                    }
                    this.border.Dispatcher.Invoke(() =>
                    {
                        Bitmap bitmap = this.mat.ToBitmap();
                        this.image.Source = BitmapToBitmapImage(bitmap);
                        bitmap.Dispose();
                        this.bar.Visibility = Visibility.Hidden;
                        this.border.Visibility = Visibility.Hidden;
                    });
                });
            }
        }

        private void Box_Click(object sender, RoutedEventArgs e)
        {
            this.drawType = DrawType.Box;
            foreach (Path item in this.addPoint)
            {
                this.imageGrid.Children.Remove(item);
            }
            foreach (Path item in this.removePoint)
            {
                this.imageGrid.Children.Remove(item);
            }
            this.addPoint.Clear();
            this.removePoint.Clear();
            Bitmap bitmap = this.mat.ToBitmap();
            this.image.Source = BitmapToBitmapImage(bitmap);
            bitmap.Dispose();
        }

        private void Point_Add_Click(object sender, RoutedEventArgs e)
        {
            this.drawType = DrawType.PointAdd;
            if (this.addPoint.Count == 0 && this.removePoint.Count == 0)
            {
                Bitmap bitmap = this.mat.ToBitmap();
                this.image.Source = BitmapToBitmapImage(bitmap);
                bitmap.Dispose();
            }
        }

        private void Point_Remove_Click(object sender, RoutedEventArgs e)
        {
            this.drawType = DrawType.PointRemove;
            if (this.addPoint.Count == 0 && this.removePoint.Count == 0)
            {
                Bitmap bitmap = this.mat.ToBitmap();
                this.image.Source = BitmapToBitmapImage(bitmap);
                bitmap.Dispose();
            }
        }

        private void PointDecode()
        {
            double scaleX = this.mat.Width / this.imageGrid.ActualWidth;
            double scaleY = this.mat.Height / this.imageGrid.ActualHeight;
            List<System.Drawing.Point> addPoints = new List<System.Drawing.Point>();
            List<System.Drawing.Point> removePoints = new List<System.Drawing.Point>();
            foreach (Path item in this.addPoint)
            {
                addPoints.Add(new System.Drawing.Point((int)(((EllipseGeometry)(item.Data)).Center.X * scaleX), (int)(((EllipseGeometry)(item.Data)).Center.Y * scaleY)));
            }
            foreach (Path item in this.removePoint)
            {
                removePoints.Add(new System.Drawing.Point((int)(((EllipseGeometry)(item.Data)).Center.X * scaleX), (int)(((EllipseGeometry)(item.Data)).Center.Y * scaleY)));
            }
            CrazyResult boxResult = this.sam.PointsDecode(addPoints, removePoints);
            Image<Gray, byte> boxResultImage = new Image<Gray, byte>(boxResult.Data);
            Image<Gray, byte> resultBox = boxResultImage.Resize(this.mat.Width, this.mat.Height, Inter.Nearest);
            boxResultImage.Dispose();
            Image<Bgr, byte> imgConvert = new Image<Bgr, byte>(this.mat.Width, this.mat.Height);
            imgConvert.SetValue(new MCvScalar(255, 0, 0), resultBox);
            resultBox.Dispose();
            Mat result = new Mat();
            CvInvoke.AddWeighted(this.mat, 1, imgConvert.Mat, 1, 0, result);
            imgConvert.Dispose();
            Bitmap bitmap = result.ToBitmap();
            this.image.Source = BitmapToBitmapImage(bitmap);
            bitmap.Dispose();
            result.Dispose();
        }

        private void Image_MouseDown(object sender, MouseButtonEventArgs e)
        {
            this.downPoint = e.GetPosition(this.imageGrid);
            this.isDown = true;
            this.imageGrid.Children.Remove(this.box);
            switch (this.drawType)
            {
                case DrawType.Box:
                    this.box.Data = null;
                    this.imageGrid.Children.Add(this.box);
                    break;
                case DrawType.PointAdd:
                    Path add = new Path { Stroke = System.Windows.Media.Brushes.Red, StrokeThickness = 5 };
                    add.Data = new EllipseGeometry { RadiusX = 1, RadiusY = 1, Center = new System.Windows.Point(this.downPoint.X, this.downPoint.Y) };
                    this.addPoint.Add(add);
                    this.imageGrid.Children.Add(add);
                    PointDecode();
                    break;
                case DrawType.PointRemove:
                    Path remove = new Path { Stroke = System.Windows.Media.Brushes.Green, StrokeThickness = 5 };
                    remove.Data = new EllipseGeometry { RadiusX = 1, RadiusY = 1, Center = new System.Windows.Point(this.downPoint.X, this.downPoint.Y) };
                    this.removePoint.Add(remove);
                    this.imageGrid.Children.Add(remove);
                    PointDecode();
                    break;
                case DrawType.None:
                default:
                    break;
            }
        }

        private void Image_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.isDown)
            {
                switch (this.drawType)
                {
                    case DrawType.Box:
                        this.box.Data = new RectangleGeometry { Rect = new Rect(this.downPoint, e.GetPosition(this.imageGrid)) };
                        break;
                    case DrawType.PointAdd:
                    case DrawType.PointRemove:
                    case DrawType.None:
                    default:
                        break;
                }
            }
        }

        private void Image_MouseUp(object sender, MouseButtonEventArgs e)
        {
            this.isDown = false;
            switch (this.drawType)
            {
                case DrawType.Box:
                    if (this.box.Data != null)
                    {
                        double scaleX = this.mat.Width / this.imageGrid.ActualWidth;
                        double scaleY = this.mat.Height / this.imageGrid.ActualHeight;
                        CrazyResult boxResult = this.sam.BoxDecode((int)(((RectangleGeometry)(this.box.Data)).Rect.Left * scaleX),
                        (int)(((RectangleGeometry)(this.box.Data)).Rect.Top * scaleY),
                        (int)(((RectangleGeometry)(this.box.Data)).Rect.Right * scaleX),
                        (int)(((RectangleGeometry)(this.box.Data)).Rect.Bottom * scaleY));
                        Image<Gray, byte> boxResultImage = new Image<Gray, byte>(boxResult.Data);
                        Image<Gray, byte> resultBox = boxResultImage.Resize(this.mat.Width, this.mat.Height, Inter.Nearest);
                        boxResultImage.Dispose();
                        Image<Bgr, byte> imgConvert = new Image<Bgr, byte>(this.mat.Width, this.mat.Height);
                        imgConvert.SetValue(new MCvScalar(255, 0, 0), resultBox);
                        resultBox.Dispose();
                        Mat result = new Mat();
                        CvInvoke.AddWeighted(this.mat, 1, imgConvert.Mat, 1, 0, result);
                        imgConvert.Dispose();
                        Bitmap bitmap = result.ToBitmap();
                        this.image.Source = BitmapToBitmapImage(bitmap);
                        bitmap.Dispose();
                        result.Dispose();
                        this.imageGrid.Children.Remove(this.box);
                    }
                    break;
                case DrawType.PointAdd:
                case DrawType.PointRemove:
                case DrawType.None:
                default:
                    break;
            }
        }
    }
}